package com.suse.common.config.redis;

import cn.hutool.core.util.BooleanUtil;
import com.suse.common.util.core.LongUtil;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Component;

import javax.annotation.Resource;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.ZoneOffset;
import java.time.format.DateTimeFormatter;
import java.util.concurrent.TimeUnit;

/**
 * <p>
 * Redis工具类
 * </p>
 *
 * @author 辜玉彬
 * @since 2022-10-22 18:02
 */
@Component
public class RedisClient extends AbstractRedisClient {

    @Resource
    private RedisTemplate<String,Object> redisTemplate;

    @Resource
    private StringRedisTemplate stringRedisTemplate;

    /**
     * 不存在（not exsit）则设置数据
     * @param key 键
     * @param value 值
     * @param ttl 过期时间
     * @param unit 时间单位
     * @return 是否设置成功
     */
    public boolean setnx(String key,Object value,long ttl,TimeUnit unit) {
        Boolean success = redisTemplate.opsForValue().setIfAbsent(key, value, ttl, unit);
        // 自动拆箱可能发生空指针异常
        return BooleanUtil.isTrue(success);
    }

    public String get(String key) {
        return stringRedisTemplate.opsForValue().get(key);
    }

    public void set(String key,Object value) {
        redisTemplate.opsForValue().set(key,value);
    }

    public void set(String key,Object value,long ttl,TimeUnit unit) {
        redisTemplate.opsForValue().set(key,value,ttl,unit);
    }

    public void set(String key,Object... values) {
        redisTemplate.opsForSet().add(key,values);
    }

    /**
     * 设置null值
     * @param key 键
     */
    public void setn(String key) {
        redisTemplate.opsForValue().set(key,DEFAULT_BLANK,DEFAULT_BLANK_TTL,DEFAULT_BLANK_TTL_UNIT);
    }

    /**
     * 设置逻辑过期（logic expire）的数据
     * @param key 键
     * @param value 值
     * @param ttl 过期时间
     * @param unit 时间单位
     */
    public void setlx(String key,Object value,long ttl,TimeUnit unit) {
        long millis = unit.toMillis(ttl);
        long currentTimeMillis = System.currentTimeMillis();
        RedisData redisData = new RedisData();
        redisData.setExpire(currentTimeMillis + millis);
        redisData.setData(value);
        redisTemplate.opsForValue().set(key, redisData);
    }

    public void del(String key) {
        redisTemplate.delete(key);
    }

    public boolean trylock(String key) {
        return trylock(key,DEFAULT_LOCK,DEFAULT_LOCK_TTL,DEFAULT_LOCK_TTL_UNIT);
    }

    /**
     * 尝试获取互斥锁
     * 设置锁和设置过期时间是原子操作保证了互斥锁的安全性
     * @param key 键
     * @param value 锁
     * @param ttl 过期时间
     * @param unit 时间单位
     * @return 获取互斥锁是否成功
     */
    public boolean trylock(String key,Object value,long ttl,TimeUnit unit) {
        Boolean success = redisTemplate.opsForValue().setIfAbsent(key, value, ttl, unit);
        // 自动拆箱可能发生空指针异常
        return BooleanUtil.isTrue(success);
    }

    /**
     * 自增
     * @param key 键
     * @return 自增之后的值
     */
    public long incr(String key) {
        Long increment = redisTemplate.opsForValue().increment(key);
        return LongUtil.longValue(increment);
    }

    /**
     * 获得一个全局唯一ID
     * @param key 键
     * @return 全局唯一ID
     */
    public long GUID(String key) {
        LocalDateTime now = LocalDateTime.now();
        long epochSecond = now.toEpochSecond(ZoneOffset.UTC);
        long timestamp = epochSecond - START_TIME_STAMP;
        DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy:MM:dd");
        String format = LocalDate.now().format(formatter);
        long incr = incr(key + ":" + format);
        return timestamp << COUNT_BITS | incr;
    }

}